预备知识:代码重定位
重定位的提出
代码重定位是把可执行代码从内存的一块区域移动到另外一块地方。但是如果指令中某些操作数没有随着地址的改变而改变,这样势必导致运行出错。如下代码:我们发现全局变量的地址包含在机器码中,而局部变量没有包含绝对地址。
重定位信息是在编译时期由编译器生成,并且保存在应用程序中,在程序执行的时候由操作系统予以修正。如果在装载时该位置已经被别的应用程序使用,操作系统会重新选择一个新的基地址。此时,就需要对所有重定位信息进行纠正,纠正的依据就是PE中的重定位表。
PE文件中的重定位表
1.重定位表的定位
重定位表位于数据目录中的第六个数据目录中。位置定位方法和前面的导入表和导出表一样。
2.重定位表项IMAGE_BASE_RELOCATION
和导入表一样,重定位表指针指向的位置是一个数组。每个数组代表的是每一个内存页的重定位信息。也就是说每个内存页的重定位信息是不同的。下面是对于IMAGE_BASE_RELOCATION结构体的介绍:
- VirtualAddress:双字,表示的是重定位块的RVA。本来一个地址需要4个字节,因为一个内存页大小诶1000h,也就是2的12次,所以只需要2个字节即可
- SizeOfBlock:双字,重定位表项中的重定位块的个数。这些数组(表项)可能不是相邻的。
- TypeOffset:表示重定位表项的类型:高四位表示的是类型。低十二位表示的重定位地址。
重定位块的大小:n*12+4+4
3.重定位表的结构
重定位表中包含有重定位块,重定位块中包含有重定位表项和前面的VirtualAddress,SizeOfBlock,TypeOffset字段(TypeOffset是一个数组,它的元素个数就是( SizeOfBlock - 8 ) / 2 ,TypeOffset 每一个元素占用两个字节即16位,其中高4位表示重定位类型(一般都为3),低12位表示重定位地址。)。
所有的重定位块最终以VirtualAddress字段为0的IMAGE_BASE_RELOCATION结构为结束标志。
4.实例分析
如下图,我们知道第一个重定位块位于0x1c00,代码起始页面RVA为00001000,块的大小为000000E8。后面的是每个重定位表项的相对位置。根据以下计算公式得到最终的实际地址。
VA=基地址(程序基地址)+代码起始页面RVA+低12位虚拟地址。
VA=01000000+00001000+009